package math2;

/**
 * The <code>LinearCombinationRnToR</code> class provides the method to
 * calculate the value at a specified point as well as derivative at a specified
 * point of a linear combination of a given function basis Rn to R.
 * 
 * @author hbui
 */
public class LinearCombinationRnToR extends FunctionRnToR {

	private double[] coeffs;
	private FunctionBasisOnRnToR basis;
	private int p = -1;

	/**
	 * Constructs the linear combination of b, which is the given function basis
	 * Rn to R.
	 * 
	 * @param b
	 *            function basis on Rn to R
	 * @return
	 */
	public LinearCombinationRnToR(PolynomialBasisOnRnToR b) {
		this.basis = b;
		this.coeffs = new double[b.getDimension()];
		this.p = b.getP();
	}

	/**
	 * Constructs the linear combination of b, which is the given function basis
	 * Rn to R.
	 * 
	 * @param b
	 *            function basis on Rn to R
	 * @return
	 */
	public LinearCombinationRnToR(FunctionBasisOnRnToR b) {
		this.basis = b;
		this.coeffs = new double[b.getDimension()];
	}

	/**
	 * Assigns value for the coefficient connected with basis function at given
	 * index.
	 * 
	 * @param idx
	 *            , c index of the basis function and given coefficient
	 * @return
	 */
	public void setCoefficient(int i, double c) {
		this.coeffs[i] = c;
	}

	/**
	 * Assigns values for the coefficients connected with basis functions.
	 * 
	 * @param c
	 *            array of coefficients
	 * @return
	 */
	public void setCoefficients(double... c) {
		if (c.length != this.basis.getDimension()) {
			throw new IllegalArgumentException("Wrong size for coefficients: Expected "
					+ this.basis.getDimension() + " but got " + c.length);
		}
		this.coeffs = c;
	}

	/**
	 * 
	 * @return coefficient array associated with the linear combination
	 */
	public double[] getCoefficients() {
		return coeffs;
	}

	@Override
	public double[] gradientAt(double... x) {
		int dim = x.length;
		double[] grad = new double[dim];
		for (int i = 0; i < this.coeffs.length; i++) {
			double[] tmp = basis.getBasisFunction(i).gradientAt(x);
			for (int j = 0; j < dim; j++) {
				grad[j] += this.coeffs[i] * tmp[j];
			}
		}
		return grad;
	}

	@Override
	public double valueAt(double... x) {
		double val = 0;
		for (int i = 0; i < this.coeffs.length; i++) {
			val += this.coeffs[i] * basis.getBasisFunction(i).valueAt(x);
		}
		return val;
	}

	@Override
	public int getP() {
		return this.p;
	}
}
